#ifndef USERDEFINE_H
#define USERDEFINE_H
#include <QString>
#include <QtWidgets>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlDriver>
#include <QSqlError>
#include <QDialog>
#include <QGraphicsDropShadowEffect>



//----------------------------------------自定义表实体相关-----------------------
/**
 * @brief The UserDefineTableEntity class 自定义表实体
 */
class UserDefineTableEntity
{
public:
    UserDefineTableEntity(){ }
    /**
     * @brief id 唯一自增id
     */
    int id;
    /**
     * @brief tableName 表名
     */
    QString tableName;
    /**
     * @brief tableZnName 表中文名
     */
    QString tableZnName;
};




//----------------------------------------自定义字段表实体相关-----------------------
/**
 * @brief The ColumnEntity class 自定义字段表实体
 */
class ColumnEntity
{
public:
    ColumnEntity() {}
    /**
     * @brief id 唯一自增id
     */
    int id;
    /**
     * @brief connectTable 关联的自定义表
     */
    QString connectTable;
    /**
     * @brief columnName 字段名
     */
    QString columnName;
    /**
     * @brief columnZnName 字段中文名
     */
    QString columnZnName;
    /**
     * @brief columnIsUnique 是否唯一
     */
    QString columnIsUnique;
    /**
     * @brief columnIsRequired 是否必填
     */
    QString columnIsRequired;
};




//----------------------------------------查询条件实体相关-----------------------
/**
 * @brief The QueryConditions class 查询条件
 */
class QueryConditions
{
public:
    /**
     * @brief QueryConditions 无参构造
     */
    QueryConditions() { }
    /**
     * @brief QueryConditions 全参构造
     * @param columnName 字段名
     * @param columnValue 字段值
     * @param isNumber 字段值是否为数字
     * @param judgmentSymbol 判断符号（=,<=,>=,>,<,!=,like%,%like,%like%）
     * @param connectionSymbol 连接符号（or，and），拼接在查询条件字段后
     */
    QueryConditions(QString columnName, QString columnValue,bool isNumber, QString judgmentSymbol, QString connectionSymbol)
    {
        this->columnName = columnName;
        this->columnValue = columnValue;
        this->isNumber = isNumber;
        this->judgmentSymbol = judgmentSymbol;
        this->connectionSymbol = connectionSymbol;
    }
    /**
     * @brief QueryConditions 构造
     * @param userDefinejudgment 自定义判断语句（如：id = 1 and name="wsd" or en_name != "234"）
     */
    QueryConditions(QString userDefinejudgment){
        this->userDefinejudgment = userDefinejudgment;
    }
    /**
     * @brief columnName 字段名
     */
    QString columnName;
    /**
     * @brief columnValue 字段值
     */
    QString columnValue;
    /**
     * @brief isNumber 字段值是否为数字
     */
    bool isNumber;
    /**
     * @brief judgmentSymbol 判断符号（=,<=,>=,>,<,!=,like%,%like,%like%）
     */
    QString judgmentSymbol;
    /**
     * @brief connectionSymbol 连接符号（or，and），拼接在查询条件字段后
     */
    QString connectionSymbol;
    /**
     * @brief userDefinejudgment 自定义判断语句（如：id = 1 and name="wsd" or en_name != "234"）
     */
    QString userDefinejudgment;
};




//----------------------------------------自定义常量类相关-----------------------
/**
 * @brief The UserDefineConstant class 自定义常量类
 */
class UserDefineConstant {
public:
    //===========主界面对象静态变量==================================
    /**
     * @brief mainWidow 主界面对象
     */
    static QMainWindow* MAIN_WINDOW;

    //===========自定义字段表相关常量==================================
    /**
     * @brief COLUMN_TABLE_NAME 自定义字段表名称
     */
    const static QString COLUMN_TABLE_NAME;
    /**
     * @brief TABLE_CONNECT_TABLE 自定义字段表中的属性字段-关联的表名
     */
    static const QString TABLE_CONNECT_TABLE;
    /**
     * @brief TABLE_COLUMN_NAME 自定义字段表中的属性字段-字段名
     */
    static const QString TABLE_COLUMN_NAME;
    /**
     * @brief TABLE_COLUMN_ZN_NAME 自定义字段表中的属性字段-字段中文名
     */
    static const QString TABLE_COLUMN_ZN_NAME;
    /**
     * @brief TABLE_COLUMN_IS_UNIQUE 自定义字段表中的属性字段-字段是否唯一
     */
    static const QString TABLE_COLUMN_IS_UNIQUE;
    /**
     * @brief TABLE_COLUMN_IS_UNIQUE 自定义字段表中的属性字段-字段是否必填
     */
    static const QString TABLE_COLUMN_IS_REQUIRED;

    /**
     * @brief TABLE_CONNECT_TABLE_ZN_TITLE （自定义字段表中的属性字段-关联的表名）中文标题
     */
    static const QString TABLE_CONNECT_TABLE_ZN_TITLE;
    /**
     * @brief TABLE_COLUMN_NAME_ZN_TITLE （自定义字段表中的属性字段-字段名）中文标题
     */
    static const QString TABLE_COLUMN_NAME_ZN_TITLE;
    /**
     * @brief TABLE_COLUMN_ZN_NAME_ZN_TITLE （自定义字段表中的属性字段-字段中文名）中文标题
     */
    static const QString TABLE_COLUMN_ZN_NAME_ZN_TITLE;
    /**
     * @brief TABLE_COLUMN_IS_UNIQUE_ZN_TITLE （自定义字段表中的属性字段-字段是否唯一）中文标题
     */
    static const QString TABLE_COLUMN_IS_UNIQUE_ZN_TITLE;
    /**
     * @brief TABLE_COLUMN_IS_REQUIRED_ZN_TITLE （自定义字段表中的属性字段-字段是否必填）中文标题
     */
    static const QString TABLE_COLUMN_IS_REQUIRED_ZN_TITLE;


    //===========自定义表相关常量==================================
    /**
     * @brief USERDEFINE_TABLE_TABLE_NAME 自定义表表名称
     */
    const static QString USERDEFINE_TABLE_TABLE_NAME;
    /**
     * @brief USERDEFINE_TABLE_COLUMN_TABLE_NAME 自定义表中属性字段-表名名称
     */
    static const QString USERDEFINE_TABLE_COLUMN_TABLE_NAME;
    /**
     * @brief USERDEFINE_TABLE_COLUMN_TABLE_ZN_NAME 自定义表中属性字段-中文表名名称
     */
    static const QString USERDEFINE_TABLE_COLUMN_TABLE_ZN_NAME;

    /**
     * @brief USERDEFINE_TABLE_COLUMN_TABLE_NAME_ZN_TITLE (自定义表中属性字段-表名名称)的中文标题
     */
    static const QString USERDEFINE_TABLE_COLUMN_TABLE_NAME_ZN_TITLE;
    /**
     * @brief USERDEFINE_TABLE_COLUMN_TABLE_ZN_NAME_ZN_TITLE (自定义表中属性字段-中文表名名称)的中文标题
     */
    static const QString USERDEFINE_TABLE_COLUMN_TABLE_ZN_NAME_ZN_TITLE;

    //===========通用性常量==================================
    /**
     * @brief ID_ZN_NAME 表格显示id的中文名
     */
    const static QString ID_ZN_NAME;
    /**
     * @brief ID_COLUMN_NUIQUE_NAME 数据库表的唯一id列名称
     */
    const static QString ID_COLUMN_NUIQUE_NAME;
    /**
     * @brief CHECK_BOX_CN_NAME 表格复选框中文名称
     */
    const static QString CHECK_BOX_CN_NAME;
    /**
     * @brief BOOL_ZN_NAME_TRUE bool为true的中文名
     */
    const static QString BOOL_ZN_NAME_TRUE;
    /**
     * @brief BOOL_ZN_NAME_FALSE bool为false的中文名
     */
    const static QString BOOL_ZN_NAME_FALSE;

    //===========内置表数据（学生表）常量==================================
    /**
     * @brief STUDENT_TABLE_NAME 学生表名称
     */
    const static QString STUDENT_TABLE_NAME;
    /**
     * @brief STUDENT_TABLE_ZN_NAME 学生表中文名称
     */
    const static QString STUDENT_TABLE_ZN_NAME;


    //===========配置setting文件相关常量和静态变量==================================
    /**
     * @brief SQL_DRIVE_SQLLITE_NAME sqllite的名称
     */
    const static QString SQL_DRIVE_SQLLITE_NAME;

    /**
     * @brief SQL_DRIVE_MYSQL_NAME mysql的名称
     */
    const static QString SQL_DRIVE_MYSQL_NAME;

    /**
     * @brief DATA_BASE_DRIVE_NAME 数据库驱动名，这里设置成静态变量，为的是可以全局直接修改
     */
    static QString DATA_BASE_DRIVE_NAME;

    /**
     * @brief DATA_BASE_NAME  数据库名称
     */
    static QString DATA_BASE_NAME;

    /**
     * @brief DATA_BASE_NAME  数据库连接ip地址
     */
    static QString DATA_BASE_HOST_NAME;

    /**
     * @brief DATA_BASE_PORT 数据库连接端口
     */
    static int DATA_BASE_PORT;

    /**
     * @brief DATA_BASE_PORT 数据库连接用户名
     */
    static QString DATA_BASE_USER_NAME;

    /**
     * @brief DATA_BASE_PORT 数据库连接密码
     */
    static QString DATA_BASE_PASS_WORD;

    /**
     * @brief PROJECT_FIRST_START_LOAD_DEFAULT_TABLE_DATA 项目第一次启动是否加载默认数据，本项目的默认数据是学生表信息
     */
    static bool PROJECT_FIRST_START_LOAD_DEFAULT_TABLE_DATA;
};




//----------------------------------------自定义窗口组件相关-----------------------
/**
 * @brief The UserDefineWidget class 自定义窗口组件，主要作用是让该窗口下的表格组件能够自适应宽度
 */
class UserDefineWidget : public QWidget
{
    Q_OBJECT

public:
    explicit UserDefineWidget(QWidget *parent = 0);
    ~UserDefineWidget();

private slots:

private:
    // 为什么采用这种方式来调整列宽，请看：https://blog.csdn.net/IT_CREATE/article/details/118643842?spm=1001.2014.3001.5501

    // 父窗体（外部传入，不应该在析构函数删除）
    QWidget *parent;
    // window启动时是否已经调整过表格
    bool isWindowHasStarted = false;
    // 窗口大小改变事件
    virtual void resizeEvent(QResizeEvent * event);
    // 图形绘制事件
    virtual void paintEvent(QPaintEvent *event);
    // 自动调整表格列宽
    void autoAdjustmentTableWidth();
    // 改变isWindowHasStarted（window启动时是否已经调整过表格）
    void changeWindowHasStartedStatus(bool status);
};




//----------------------------------------自定义单元格信息类相关-----------------------
/**
 * @brief The CellInfo class 自定义单元格信息类,主要用作单元格内容修改时保存的一些信息，用作判断
 */
class CellInfo {
public:
    CellInfo(){}
    /**
     * @brief isDoubleClick 是否双击单元格
     */
    bool isDoubleClick;
    /**
     * @brief row 单元格所在行
     */
    int row;
    /**
     * @brief column 单元格所在列
     */
    int column;
    /**
     * @brief text 单元格内容
     */
    QString text;
};



//----------------------------------------对象工具类相关-----------------------
/**
 * @brief The ObjectUtil class 对象工具类
 */
class ObjectUtil {
public:
    const static bool isEmpty(QString str) {
        return str.isNull() || str.trimmed().isEmpty();
    }
    template <typename K,typename V> const static bool isEmpty(QMap<K,V> map) {
        return map.isEmpty();
    }
    template <typename T> const static bool isEmpty(QList<T> list) {
        return list.isEmpty();
    }
    const static bool isNull(QObject* obj) {
        return obj == NULL || obj == nullptr;
    }
};


//----------------------------------------数据库管理对象相关-----------------------
/**
 * @brief The DataBaseManage class 数据库管理对象
 */
class DataBaseManage {
public:
    static QSqlDatabase db;

    static QSqlDatabase getDb();

    static void openDb();

    static QSqlQuery getQuery();
};




//----------------------------------------数据库操作静态函数相关-----------------------
/**
 * @brief The DataBaseOpt class 数据库操作类，内置了一些列操作数据库的方法
 */
class DataBaseOpt {
public:
    /**
     * @brief createTable 创建表方法
     * @param tableName 表名
     * @param columnInfos 字段信息集合
     * @return 成功与否
     */
    const static bool createTable(QString tableName, QList<QString> columnInfos);

    /**
     * @brief createTable 创建表方法
     * @param tableName 表名
     * @param columnInfos 字段信息集合
     * @param columnInfosEmptyAlsoCreate 字段信息集合为空是否也要创建
     * @return 成功与否
     */
    const static bool createTable(QString tableName, QList<QString> columnInfos, bool columnInfosEmptyAlsoCreate);

    /**
     * @brief removeTable 删除表方法
     * @param tableName 表名
     * @return 成功与否
     */
    const static bool removeTable(QString tableName);

    /**
     * @brief renameTable 重命名表名方法
     * @param oldTableName 老的表名
     * @param newTableName 新的表名
     * @return 成功与否
     */
    const static bool renameTable(QString oldTableName, QString newTableName);

    /**
     * @brief copyOldTableDataToNewTable 将老表中的数据拷贝到新表中
     * @param oldTableName 老的表名
     * @param oldColumnNames 老的表对应的列
     * @param newTableName 新的表名
     * @param newColumnNames 新的表对应的列
     * @return 成功与否
     */
    const static bool copyOldTableDataToNewTable(QString oldTableName, QList<QString> oldColumnNames, QString newTableName, QList<QString> newColumnNames);

    /**
     * @brief addColumnForTable 向指定表中增加列
     * @param tableName 表名
     * @param columnNames 列名集合
     * @return 是否成功
     */
    const static bool addColumnForTable(QString tableName, QList<QString> columnNames);

    /**
     * @brief updateColumnForTable 修改指定表的列
     * @param tableName 表名
     * @param oldColumnNameAndNewColumnName 老字段名和新字段名的映射关系
     * @return 是否成功
     */
    const static bool updateColumnForTable(QString tableName, QMap<QString, QString> oldColumnNameAndNewColumnName);

    /**
     * @brief deleteColumnForTable 删除指定表的列
     * @param tableName 表名
     * @param columnNames 列名集合
     * @return 是否成功
     */
    const static bool deleteColumnForTable(QString tableName, QList<QString> columnNames);

    /**
     * @brief updateTbaleName 修改表的名字
     * @param oldTableNameAndNewTableName 老表名和新表名的映射关系
     * @return 是否成功
     */
    const static bool updateTableName(QMap<QString, QString> oldTableNameAndNewTableName);

    /**
     * @brief selectAllCount 根据条件查询数据总数
     * @param tableName 表名
     * @param conditions 条件集合
     * @return 根据条件查询到的数据总数
     */
    const static int selectAllCount(QString tableName, QList<QueryConditions> conditions);

    /**
     * @brief selectTable 根据条件查询数据库数据
     * @param tableName 表名
     * @param columnNames 返回字段集合
     * @param conditions 条件集合
     * @return 查询出的数据集合
     */
    const static QList<QMap<QString,QString>> selectTable(QString tableName, QList<QString> columnNames,QList<QueryConditions> conditions);

    /**
     * @brief updateTableById 修改数据库数据
     * @param tableName 表名
     * @param id 对应数据id
     * @param columnNameAndValues 待修改的字段名和值的映射关系
     * @return  是否执行成功
     */
    const static bool updateTableById(QString tableName, int id, QMap<QString, QString> columnNameAndValues);

    /**
     * @brief insertTableData 向数据库中插入数据
     * @param tableName 表名
     * @param columnNameAndValues 字段与值的映射关系
     * @return 是否执行成功
     */
    const static bool insertTableData(QString tableName, QList<QPair<QString,QString>> columnNameAndValues);

    /**
     * @brief updateTableDataByCondition 修改数据库中的数据
     * @param tableName 表名
     * @param columnNameAndValues 字段与值的映射关系
     * @param conditions where条件
     * @return 是否执行成功
     */
    const static bool updateTableDataByCondition(QString tableName, QMap<QString, QString> columnNameAndValues, QList<QueryConditions> conditions);

    /**
     * @brief deleteTableDataByCondition 根据条件删除表数据
     * @param tableName 表名
     * @param conditions where条件
     * @return 是否执行成功
     */
    const static bool deleteTableDataByCondition(QString tableName, QList<QueryConditions> conditions);

    /**
     * @brief removeTableByIds 根据id集合删除表数据
     * @param tableName 表名
     * @param ids id集合
     * @return 是否执行成功
     */
    const static bool removeTableByIds(QString tableName, QList<int> ids);

    /**
     * @brief getSet 获取sql中修改数据时的set对应的语句
     * @param columnNameAndValues 字段名和值的映射关系
     * @return  sql中set对应的语句字符串(比如：name="hello",class="二班")
     */
    const static QString getSet(QMap<QString, QString> columnNameAndValues);

    /**
     * @brief getWhere 获取sql的where条件语句
     * @param conditions 条件集合
     * @return 解析拼接过后的where条件字符串(比如：name="hello" and age=15)
     */
    const static QString getWhere(QList<QueryConditions> conditions);

    /**
     * @brief getStartIndex 根据页码和单页数据量获取数据库对应起始索引
     * @param page 页码
     * @param size 单页数据量
     * @return 数据库对应起始索引
     */
    const static int getStartIndex(int page, int size);

    /**
     * @brief createUserDefineSqlTable 创建自定义表
     * @return 成功与否
     */
    const static bool createUserDefineSqlTable();

    /**
     * @brief selectAllUserDefineTable 获取自定义表里的所有数据
     * @param conditions where查询条件
     * @return 自定义表数据
     */
    const static QList<UserDefineTableEntity> selectAllUserDefineTable(QList<QueryConditions> conditions);

    /**
     * @brief selectUserDefineTableByIds 通过id集合查询自定义表里的数据
     * @param ids id集合
     * @return 自定义表数据
     */
    const static QList<UserDefineTableEntity> selectUserDefineTableByIds(QList<int> ids);

    /**
     * @brief createColumnSqlTable 创建字段表
     * @return 成功与否
     */
    const static bool createColumnSqlTable();

    /**
     * @brief selectAllColumnTable 获取自定义字段表里的所有数据
     * @param conditions where查询条件
     * @return 自定义字段表里的所有数据
     */
    const static QList<ColumnEntity> selectAllColumnTable(QList<QueryConditions> conditions);

    /**
     * @brief selectColumnTableByIds 通过id集合查询自定义字段表里的数据
     * @param ids id集合
     * @return 字段表数据
     */
    const static QList<ColumnEntity> selectColumnTableByIds(QList<int> ids);
};




//----------------------------------------表格组件操作静态函数相关-----------------------
/**
 * @brief The TableWidgetOpt class 表格操作类，内置了一些直接操作指定表格数据/显示等方法
 */
class TableWidgetOpt {
public:
    /**
     * @brief getTableWidgetCheckedIds 获取表格选中行的id列数据集合
     * @param widget 表格
     * @return 已选中的id集合
     */
    const static QList<int> getQTableWidgetCheckedIds(QTableWidget* widget);

    /**
     * @brief findQTableWidgetIdZnNameColumn 找出表格中id所在列的位置
     * @param widget 表格
     * @return id所在列的位置
     */
    const static int findQTableWidgetIdZnNameColumn(QTableWidget* widget);

    /**
     * @brief createQTableWidgetHeader 创建表格列表头信息
     * @param tableWidget 指定表格
     * @param columnNames 列表头信息集合
     */
    const static void createQTableWidgetHeader(QTableWidget* tableWidget, QList<QString> columnNames);

    /**
     * @brief selectDataBaseTableDataToQTableWidget 查询数据库数据并向指定列表插入数据
     * @param tableWidget 需要插入数据的表格
     * @param tableName 表名
     * @param columnNames 需要查询的数据库表对应的字段集合
     * @param page 页码，从1开始，比1要大
     * @param size 单页展示的数据量
     * @param conditions 查询条件集合
     * @param ispaging 是否分页，开启分页page、size才生效
     * @return 根据条件查询到的数据总数
     */
    const static int selectDataBaseTableDataToQTableWidget(QTableWidget *tableWidget,QString tableName, QList<QString> columnNames, int page, int size, QList<QueryConditions> conditions, bool ispaging);

    /**
     * @brief autoAdjustmentQTableWidgetWidth 自动调整表格列宽
     * @param tableWidget 列表对象
     * @param defaultMinWidthParam 默认最小自动分配的表格宽度，表示的是当显示表格的宽度比这个值小的时候就不会进行自动分配宽度了,而是会以横向滚动条显示
     * @param defaultMinItemWidthParam 自动分配时默认最小时单列的宽度，当自动分配是计算出的平均列宽小于该值，会采用该值进行设置
     * @param columnWidthMap 用户自定义设置列宽，key为表格列索引，从0开始，value表示设置改列的宽度，当设置的有效列数量和表格总列数相同时，自动分配就会失效
     */
    const static void autoAdjustmentQTableWidgetWidth(QTableWidget *tableWidget,int defaultMinWidthParam,int defaultMinItemWidthParam,QMap<int,int> columnWidthMap);
};



//----------------------------------------加载弹出框相关----------------------------------------------------
/**
 * @brief The LoadingDialog class 加载弹出框，数据加载过程中用来做提示的
 */
class LoadingDialog : public QDialog
{
    Q_OBJECT
public:
    explicit LoadingDialog(QWidget *parent = nullptr);
    explicit LoadingDialog(bool isLoadCountDown, QWidget *parent = nullptr);
    ~LoadingDialog();

    /**
     * @brief createLoading 创建一个加载弹出层
     * @param parent 父对象
     * @return 加载弹出层
     */
    static LoadingDialog* createLoading(QWidget *parent = nullptr);

    /**
     * @brief createLoading 创建一个加载弹出层
     * @param isLoadCountDown 是否需要倒计时功能,设置为true，则表示需要倒计时，则倒计时到达后显示可以取消弹出框的按钮，false则直接显示可取消的按钮，没有倒计时
     * @param parent 父对象
     * @return 加载弹出层
     */
    static LoadingDialog* createLoading(bool isLoadCountDown, QWidget *parent = nullptr);

protected:
    /**
     * @brief paintEvent 界面绘制
     * @param event 绘制事件
     */
    virtual void paintEvent(QPaintEvent *event) override;

private slots:
    /**
     * @brief cancelBtnClicked 取消按钮槽函数
     */
    void cancelBtnClicked();
    /**
     * @brief timerOpt 定时操作的方法
     */
    void timerOpt();

private:
    // 是否需要倒计时功能,设置为true，则表示需要倒计时，则倒计时到达后显示可以取消弹出框的按钮，false则直接显示可取消的按钮，没有倒计时
    bool isLoadCountDown = true;
    // 定时器(用来做计时和修改取消按钮的状态)
    QTimer *waitTimer = NULL;
    // 用时时长，单位秒
    int timeCount = 0;
    // 等待时常，8秒
    int waitTime = 8;
    // 中心区域
    QFrame *m_pCenterFrame;
    // 显示gif动图的label
    QLabel *m_pMovieLabel;
    // 加载gif动图的
    QMovie *m_pLoadingMovie;
    // 提示信息的label
    QLabel *m_pTipsLabel;
    // 取消按钮
    QPushButton *m_pCancelBtn;

    /**
     * @brief initUi UI元素初始化
     */
    void initUi();
};

//----------------------------------------提示信息实体相关----------------------------------------------------
/**
 * @brief The MessageInfo class 提示信息实体
 */
class MessageInfo {
public:
    MessageInfo(){}
    MessageInfo(QString title, QString msg) {
        this->title = title;
        this->msg = msg;
    }
    /**
     * @brief title 标题
     */
    QString title;
    /**
     * @brief msg 提示信息
     */
    QString msg;
};
#endif // USERDEFINE_H
